Skip to content

Anti-loop: forward DRY sampler + forward-only nudges (0.3.4)#38

Merged
mjwsolo merged 5 commits into
mainfrom
fix/forward-dry-samplers
Jul 6, 2026
Merged

Anti-loop: forward DRY sampler + forward-only nudges (0.3.4)#38
mjwsolo merged 5 commits into
mainfrom
fix/forward-dry-samplers

Conversation

@mjwsolo

@mjwsolo mjwsolo commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Root-cause fixes for model looping, found by reviewing real localcode logs of the Anki build. 634 tests pass.

Server config: actually forward DRY

The /v1/chat/completions payload forwarded ONLY temperature + repeat_penalty=1.05. The full anti-repetition config (DRY, min_p) was computed into opts but only reached the DEAD ollama-shaped payload (ollama was removed), so it never hit the server. llama.cpp defaults DRY OFF → the model ran with no n-gram repetition penalty. That's a real root cause of the repeat-collapse loops.

Fix: forward the EOS-neutral anti-loop samplers — DRY (penalizes repeated n-grams, not single tokens, so it doesn't suppress EOS like repeat_penalty) + min_p. Deliberately NOT re-adding the aggressive repeat_penalty/top_k/top_p bundle that caused the 2026-04-29 paraphrase regression. Instant A/B revert with LOCALCODE_SAMPLER_MINIMAL=1. Also logs the actual forwarded samplers once per session so looping can be correlated with real config.

Code: forward-only recovery nudges

Logs showed the model stuck repeating "I've been going in circles. Let me build this ENTIRE app NOW" while making tiny progress. Cause: churn/spin nudges were injected as role:"user" messages with self-critical loop language ("you're going in circles", "you've rewritten X N times"). A small model reads that as the USER speaking, replies "You're right, I've been going in circles" — which then self-conditions the loop deeper (arXiv:2509.09677).

Fix: reworded all four churn nudges to forward-only imperatives (next concrete action, zero echo-able language about the failure).

mjwsolo added 5 commits July 6, 2026 09:01
Root-cause investigation into model looping: the /v1/chat/completions payload
forwarded ONLY temperature + repeat_penalty=1.05. The full anti-repetition
config (DRY, min_p, top_k, top_p) was computed into `opts` but only reached the
DEAD ollama-shaped payload (ollama was removed) — so it never hit the server.
llama.cpp has DRY OFF by default, so in production the model ran with near-
default sampling and NO DRY. That's a real root cause of the repeat-collapse
loops (the original "gemma garbage repeat" class).

Fix: forward the EOS-NEUTRAL anti-loop samplers — DRY (penalises repeated
N-grams, not single tokens, so it doesn't suppress EOS like repeat_penalty)
and min_p (trims the collapse-feeding low-prob tail). Deliberately NOT re-adding
the aggressive repeat_penalty/top_k/top_p bundle that caused the 2026-04-29
paraphrase regression. A/B revert with LOCALCODE_SAMPLER_MINIMAL=1.

Also: log the ACTUAL forwarded samplers once per session (was opt-in behind
LOCALCODE_DEBUG_SAMPLERS) so looping can always be correlated with the real
server config — the "control this better" record.
…tioning loop)

Log analysis of the Anki build showed the model stuck repeating "I've been
going in circles. Let me build this ENTIRE app NOW" ~10x while making tiny
progress. Root cause: our churn/spin nudges were injected as role:"user"
messages containing self-critical loop language ("you're going in circles",
"you've rewritten X N times", "planned enough"). A small model reads that as
the USER speaking and replies "You're right, I've been going in circles" — which
then sits in its own context and self-conditions the loop deeper
(arXiv:2509.09677: a model that sees its own error/loop text produces more of it).

Reworded all four churn nudges to FORWARD-ONLY imperatives: tell the model the
next concrete action, with zero echo-able language about the failure it just had.
Nothing for it to parrot back into its own context.
… on disconnect

The recurring mid-stream -9 crashes were undiagnosable because the runtime
restart path sent the server's stdout/stderr to /dev/null — so llama-server's
own final error (ggml/Metal allocation failure, GGML_ASSERT, OOM) was discarded
and we only saw httpx's generic 'connection dropped'. Only first-launch setup
captured it.

Now every spawn appends to ~/.local/share/localcode/server.log, and
_log_disconnect_context reads the last 15 lines into the server_disconnect
event + last_error.log — so the NEXT crash records the actual reason instead of
leaving us guessing.
@mjwsolo mjwsolo merged commit 54a7a8f into main Jul 6, 2026
1 of 6 checks passed
@mjwsolo mjwsolo deleted the fix/forward-dry-samplers branch July 6, 2026 21:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant